import datetime
import imghdr
import os

import cv2
import face_recognition
import numpy as np
from mtcnn import MTCNN  # 导入MTCNN检测器
from PIL import Image, ImageDraw, ImageFont


detector = MTCNN()
print("lz4版本BUG报错 ValueError: I/O operation on closed file.Exception ignored in: <_io.BufferedReader>,无需担心")


def load_face_encodings(data_dir):
    """加载人脸特征编码（保留原有逻辑，增加预处理）"""
    encodings = {}
    for root, dirs, files in os.walk(data_dir):
        for person in files:
            image_path = os.path.join(root, person)
            try:
                # cv2.imread不支持中文路径,改为使用imdecode
                # image = cv2.imread(image_path)
                if imghdr.what(image_path) == "png":
                    image = np.array(Image.open(image_path).convert("RGB"))
                else:
                    image = cv2.imdecode(np.fromfile(image_path),cv2.IMREAD_COLOR)
                if image is None:
                    print(f"警告: 无法加载图像 {image_path}")
                    continue

                # 预处理：转换为RGB并归一化
                image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
                # 使用MTCNN检测人脸（确保训练图中人脸质量）
                results = detector.detect_faces(image_rgb)
                if not results:
                    print(f"警告: {image_path} 中未检测到人脸，已跳过")
                    continue

                # 提取人脸区域（取置信度最高的人脸）
                face = max(results, key=lambda x: x['confidence'])
                x1, y1, width, height = face['box']
                x2, y2 = x1 + width, y1 + height
                face_img = np.ascontiguousarray(image_rgb[y1:y2, x1:x2])

                # 生成特征编码
                encoding = face_recognition.face_encodings(face_img)
                if not encoding:
                    print(f"警告: {image_path} 中未检测到人脸，已跳过")
                    continue
                encodings[str(person.split('.')[0])] = encoding[0]
            except Exception as e:
                print(f"处理 {image_path} 时出错: {str(e)}")
    return encodings


def mark_attendance(name):
    # """记录考勤（同上，可增加去重逻辑）"""
    # with open('attendance.csv', 'a') as f:
    #     current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    #     f.write(f'{name},{current_time}\n')
    print(f'{name} 已打卡成功！')

def_font = None
def cv2_put_chinese_text(img, text, position, font_size=20, color=(255, 255, 255)):
    """
    在OpenCV图像上绘制中文文字（解决乱码问题）

    参数:
        img: OpenCV图像（BGR格式）
        text: 要绘制的中文文字
        position: 文字左上角坐标 (x, y)
        font_size: 字体大小
        color: 文字颜色（BGR格式，默认白色）

    返回:
        绘制了文字的图像
    """
    # 转换为PIL图像（RGB格式）
    img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

    # 加载中文字体（需确保字体文件存在）
    # 这里使用系统默认中文字体，也可指定字体文件路径
    try:
        # 尝试加载系统中的黑体字体（Windows/Linux/macOS通用）
        # todo 黑体是有版权的,实际使用时请更换
        font = ImageFont.truetype("simhei.ttf", font_size) if def_font is None else def_font
    except IOError:
        try:
            # 备选字体（macOS）
            font = ImageFont.truetype("Heiti TC", font_size)
        except IOError:
            # 若找不到字体，使用默认字体（可能仍会乱码）
            font = ImageFont.load_default()
            print("警告：未找到中文字体，可能显示乱码")

    # 绘制文字
    draw = ImageDraw.Draw(img_pil)
    draw.text(position, text, font=font, fill=(color[2], color[1], color[0]))  # 转换为RGB格式

    # 转回OpenCV格式（BGR）
    return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)



def search_face():
    # 主程序
    def_data_dir = 'attendance_photos'
    face_encodings = load_face_encodings(def_data_dir)
    # 读取视频文件
    # cap = cv2.VideoCapture('example.mp4')
    # 打开摄像头
    cap = cv2.VideoCapture(0)

    while True:
        ret, frame = cap.read()
        if not ret:
            print("无法获取摄像头画面")
            break

        # 预处理：转换为RGB（MTCNN需要RGB输入）
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        # 用MTCNN检测人脸（高精度检测）
        results = detector.detect_faces(frame_rgb)

        # 遍历检测到的人脸
        for result in results:
            # 提取人脸位置和置信度
            x1, y1, width, height = result['box']
            x2, y2 = x1 + width, y1 + height
            confidence = result['confidence']

            # 过滤低置信度人脸（提升精度，减少误检）
            if confidence < 0.9:
                continue

            # 提取人脸区域用于识别
            face_img = np.ascontiguousarray(frame_rgb[y1:y2, x1:x2])
            face_encodings_frame = face_recognition.face_encodings(face_img)
            if not face_encodings_frame:
                continue
            face_encoding = face_encodings_frame[0]

            # 人脸识别与考勤
            matches = face_recognition.compare_faces(list(face_encodings.values()), face_encoding,
                                                     tolerance=0.5)  # 降低容错率（0.5比默认0.6更严格）
            name = "Unknown"
            if True in matches:
                first_match_index = matches.index(True)
                name = list(face_encodings.keys())[first_match_index]
                mark_attendance(name)

            # 绘制人脸框和姓名（绿色框，更粗的线条）
            cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 3)  # 线宽增加到3
            cv2.rectangle(frame, (x1, y1 - 35), (x2, y1), (0, 255, 0), cv2.FILLED)
            # cv2.puttext不支持中文,通过Pillow转换成位图添加上去
            # cv2.putText(frame, name, (x1 + 6, y1 - 6), cv2.FONT_HERSHEY_DUPLEX, 1.0, (255, 255, 255), 2)  # 文字加粗
            frame = cv2_put_chinese_text(frame, name, (x1 + 6, y1 - 35), font_size=30, color=(255, 255, 255))

        cv2.imshow('High-Precision Face Attendance', frame)
        # 参数 0 表示无限等待，直到用户按下任意键
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    cap.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
    search_face()
    # print("main")